package com.msi.visualization;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Paint;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections15.Transformer;
import org.apache.commons.collections15.functors.MapTransformer;

import edu.uci.ics.jung.algorithms.layout.FRLayout2;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.algorithms.layout.StaticLayout;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.SparseMultigraph;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
import edu.uci.ics.jung.visualization.control.GraphMouseListener;
import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
import edu.uci.ics.jung.visualization.renderers.Renderer.VertexLabel.Position;
import com.msi.Vertex;

public class GraphVisualization extends VisualizationViewer<Integer, String> {
	private class EdgeTransf implements Transformer<String, Paint> {
		private Integer vertex;

		public EdgeTransf(Integer vertex) {
			this.vertex = vertex;
		}

		@Override
		public Paint transform(String s) {
			if (g.getInEdges(vertex).contains(s))
				return Color.green;
			return null;
		}

	}

	private static GraphVisualization instance;
	private static final int LAYOUT_HEIGHT = 560;

	private static final int LAYOUT_WIDTH = 560;
	private static final long serialVersionUID = 1L;

	public static GraphVisualization createGraphVisualization() {
		Graph<Integer, String> g = new SparseMultigraph<Integer, String>();
		Layout<Integer, String> layout = new FRLayout2(g);

		instance = new GraphVisualization(g, layout);
		return instance;
	}

	public static GraphVisualization getInstance() {
		if (instance == null)
			createGraphVisualization();
		return instance;
	}

	private Graph<Integer, String> g;

	private com.msi.Graph graph = null;

	private Layout<Integer, String> layout;

	private GraphVisualization(final Graph<Integer, String> g,
			Layout<Integer, String> layout) {
		super(layout);
		this.g = g;
		this.layout = layout;

		getRenderContext().setVertexLabelTransformer(new ToStringLabeller());
		getRenderer().getVertexLabelRenderer().setPosition(Position.CNTR);

		setGraphMouse(new DefaultModalGraphMouse<Integer, String>());

		addGraphMouseListener(new GraphMouseListener<Integer>() {

			@Override
			public void graphClicked(Integer arg0, MouseEvent arg1) {
				// do nothing
			}

			@Override
			public void graphPressed(final Integer vertex, MouseEvent arg1) {
				getRenderContext().setEdgeDrawPaintTransformer(
						new EdgeTransf(vertex));
				repaint();
			}

			@Override
			public void graphReleased(Integer arg0, MouseEvent arg1) {
				// do nothing
			}
		});
	}

	public void buildGraph(com.msi.Graph graph) {
		clearGraph();
		this.graph = graph;
		initializeCoordinates();
		List<Vertex> vertexes = graph.getVerticesSortedByDegree();
		for (Vertex v : vertexes) {
			g.addVertex(v.getId());
		}
		for (Vertex v : vertexes) {
			List<Vertex> neighborhood = v.getNeighborhood();
			for (Vertex v2 : neighborhood) {
				if (!g.isNeighbor(v.getId(), v2.getId())) {
					g.addEdge(v.getId() + "-" + v2.getId(), v.getId(),
							v2.getId());
				}
			}
		}
	}

	public void changeLayout(Layout<Integer, String> layout) {
		setGraphLayout(layout);
		this.layout = layout;
	}

	public void useAutoLayout() {
		changeLayout(new FRLayout2(g));
	}

	private void clearGraph() {
		getRenderContext().setEdgeDrawPaintTransformer(
				getDefaultEdgeTransformer());
		getRenderContext().setVertexFillPaintTransformer(
				getDefaultColorTransformer());
		g = new SparseMultigraph<Integer, String>();
		graph = null;
	}

	private Transformer<Integer, Paint> getDefaultColorTransformer() {
		return new Transformer<Integer, Paint>() {

			@Override
			public Paint transform(Integer arg0) {
				return Color.red;
			}
		};
	}

	private Transformer<String, Paint> getDefaultEdgeTransformer() {
		return new Transformer<String, Paint>() {

			@Override
			public Paint transform(String arg0) {
				return Color.black;
			}
		};
	}

	public com.msi.Graph getGraph() {
		return graph;
	}

	private void initializeCoordinates() {
		if (graph.hasCoordinates()) {
			Transformer<Integer, Point2D> tr = MapTransformer.getInstance(graph
					.getCoordinateMap());
			changeLayout(new StaticLayout(g, tr, new Dimension(100, 100)));
		} else {
			changeLayout(new FRLayout2(g));
		}
	}

	public void updateColors(final MsiColor impl) {
		final Map<Integer, Color> colors = new ColorMap(
				impl.getNumberOfColors());
		Transformer<Integer, Paint> vertexPaint = new Transformer<Integer, Paint>() {
			@Override
			public Paint transform(Integer i) {
				return colors.get(graph.getVertex(i).getColor());
			}
		};
		getRenderContext().setVertexFillPaintTransformer(vertexPaint);
	}
}
